<?php
/**
 * ファイル添付一括管理モジュール Upload
 */
class Upload {

	public function init () {
		// セッションを開始する
		Y('Helper.loadAction', 'session');

		Y('this.listFile+', YuTori::Create($this, 'listFile'));
		Y('this.isFileExist+', YuTori::Create($this, 'isFileExist'));
		Y('this.deleteFile+', YuTori::Create($this, 'deleteFile'));

		Y('this.Cache+', YuTori::Create($this, 'Cache'));

		Y('this.uploadFile+', YuTori::Create($this, 'uploadFile'));
		Y('this.deleteFileTmp+', YuTori::Create($this, 'deleteFileTmp'));
		Y('this.saveFile+', YuTori::Create($this, 'saveFile'));

		Y('this.admin+', YuTori::Create($this, 'callAdmin'));
	}

	public function call () {
	}

	/**
	 * キャッシュコール
	 */
	public function cache () {
		// 必要なデータが多いのでグローバルリクエストシフトしまくる
		$Data['Target'] = Y('Request.Global.Act'); Y('Request.Global');
		$Data['Field'] = Y('Request.Global.Act'); Y('Request.Global');
		$Data['TargetID'] = Y('Request.Global.Act'); Y('Request.Global');
		$Data['Size'] = Y('Request.Global.Act');
		$Data['Name'] = Y('Request.Global.Op');

		// ファイルID取得
		Y('Helper.loadAction','ConnectDB', array(
			'query'=>'select',
			'table'=>'upload_table',
			'data'=>$Data,
			'column'=>'ID,FileSize,RegistTime',
		));
		if ($File = Y('Helper.List.ConnectDB.Com.Result')) {
			// ファイル実態取得
			Y('Helper.list.ConnectDB', array(
				'query'=>'select',
				'table'=>'upload_table',
				'data'=>$File,
			));
			$File = Y('Helper.List.ConnectDB.Com.Result');

			// 画像処理を行う
			$File['Binary'] = base64_decode($File['Binary']);
			$SrcImage = imagecreatefromstring ($File['Binary']);

			// 画像でない場合はダウンロード
			if ($SrcImage == false) {
				// TODO: 現状はapplication/octet-streamを出力するのは.exeのみなので、強引に.exe扱い。
				// 後でoutputを修正して、DLモードを作成するように。
				Y('Output.Name', $File['Name'].'.exe');
				header ("Content-Disposition: attachment; filename=".$File['Name']);
				
				Y('Output.Message', $File['Binary']);
				$this->setCacheFile ($Data, $File['Binary']);
				return true;
			}
			// TODO: このへんにキャッシュ保存処理をつける。

			// オリジナルサイズ指定の時はそのまま出力する
			if (strcasecmp($Data['Size'], 'original') === 0) {
				Y('Output.Name', $File['Name']);
				Y('Output.Message', $File['Binary']);
				$this->setCacheFile ($Data, $File['Binary']);
				return true;
			}

			// リクエストサイズの判別
			$Size = explode ('x', $Data['Size'], 2);
			if (count($Size) < 2) return false;
			$Size = array(
				'x'=>(int)$Size[0],
				'y'=>(int)$Size[1]
			);

			// 画像のサイズ判別
			$SrcX = imagesx ($SrcImage);
			$SrcY = imagesy ($SrcImage);

			// XとYのどちらの幅を拡大縮小の基準にするか
			$RateX = $Size['x'] / $SrcX;
			$RateY = $Size['y'] / $SrcY;

			// 拡大縮小の比率が小さくなる方に合わせる
			if ($RateX < $RateY) $Rate = $RateX;
			else $Rate = $RateY;

			// $Rateが1以上の場合、オリジナルサイズになる。
			if ($Rate >= 1) {
				Y('Output.Name', $File['Name']);
				Y('Output.Message', $File['Binary']);
				$this->setCacheFile ($Data, $File['Binary']);
				return true;
			}

			$DstX = (int)($SrcX * $Rate);
			$DstY = (int)($SrcY * $Rate);

			// メモリ節約のため、元のバイナリはunset
			unset ($File['Binary']);
			
			$DstImage = imagecreatetruecolor ($DstX, $DstY);
			
			imagecopyresized($DstImage, $SrcImage, 0, 0, 0, 0, $DstX, $DstY, $SrcX,$SrcY);
			imagedestroy ($SrcImage);
			
			ob_start ();
			imagejpeg ($DstImage, null, 80);
			imagedestroy ($DstImage);
			$Image = ob_get_contents();
			ob_end_clean();

			// ザ・出力
			Y('Output.Name', $File['Name'].'.jpg');
			Y('Output.Message', $Image);
			
			$this->setCacheFile ($Data, $Image);

			return true;
		} else {
			return false;
		}
	}

	/**
	 * キャッシュファイルの作成
	 */
	private function setCacheFile ($Data, &$Binary) {
		// キャッシュファイル作成
		if (!is_dir ($Dir = DIR_BASE_HTML.'/vars/cache/upload/'.$Data['Target'].'/'. $Data['Field']. '/'. $Data['TargetID']. '/'. $Data['Size'])) {
			mkdir ($Dir, 0777, true);
		}
		file_put_contents ($Dir.'/'. $Data['Name'], $Binary);
		return true;
	}

	/**
	 * アップロード済みのファイル一覧を取得する
	 * FieldNameに「*」を指定すると全フィールドを取得する
	 */
	public function listFile ($FieldName, $Target, $TargetID = 0) {
		$Data = array();
		if ($Target != '*') $Data['Target']=$Target;
		if ($FieldName != '*') 	$Data['Field']  = $FieldName;
		if ($TargetID !== 0) $Data['TargetID']  = $TargetID;

		Y('Helper.list.ConnectDB', array(
			'query'=>'select',
			'table'=>'upload_table',
			'data'=>$Data,
			'column'=>'ID,Target,Field,TargetID,Name,FileSize,RegistTime',
		));
		return Y('Helper.List.ConnectDB.com.ResultAll');
	}

	/**
	 * 指定されたファイルがアップロード済みかどうか調べる
	 * 存在していればID,Name,FileSize,RegistTimeを持つ配列を返す。
	 * 存在していなければfalse。
	 * FieldNameに「*」を指定すると全フィールドを取得する
	 */
	public function isFileExist ($FileName, $FieldName, $Target, $TargetID = 0) {
		$Data = array('Name'=>$FileName);
		if ($FieldName != '*') 	$Data['Field']  = $FieldName;
		if ($Target != '*') 	$Data['Target']  = $Target;
		if ($TargetID !== 0) $Data['TargetID']  = $TargetID;

		Y('Helper.list.ConnectDB', array(
			'query'=>'select',
			'table'=>'upload_table',
			'data'=>$Data,
			'column'=>'ID,Target,Field,TargetID,Name,FileSize,RegistTime',
			'limit'=>1,
		));
		return Y('Helper.List.ConnectDB.com.Result');
	}


        /**
         * アップロードファイルをテンポラリに保存する
         */
        public function uploadFile ($FieldName, $Target) {
                // アップロードファイルが正しいか判定
                if (!isset($_FILES[$FieldName]) || !is_uploaded_file($_FILES[$FieldName]['tmp_name'])) {
                        return array('error'=>'Invalid Upload');
                }
                if (!$SID = Y('Request.Post.session')) {
                        if (!$SID = Y('Helper.list.Session.ID')) {
                                return array('error'=>'Session Not Found');
                        }
                }

                $Data = array(
                        'ID'=>0,
                        'Session'=>$SID,
                        'Target'=>$Target,
                        'Field'=>$FieldName,
                        'Name'=>$_FILES[$FieldName]['name'],
                        'FileSize'=>filesize($_FILES[$FieldName]['tmp_name']),
                        'RegistTime'=>time(),
                        'Binary'=>base64_encode(file_get_contents($_FILES[$FieldName]['tmp_name']))
                );

                $Result = Y('Helper.loadAction', 'ConnectDB', array(
                        'query'=>'insert',
                        'table'=>'upload_tmp',
                        'data'=>$Data,
                ));

                if ($Result) {
                        return array('error'=>null,'filesize'=>$Data['FileSize'], 'filename'=>$Data['Name']);
                } else {
                        return array('error'=>407,'filesize'=>0, 'filename'=>$Data['Name']);
                }
        }

	/**
	 * テンポラリ上のファイルを削除する
	 */
	public function deleteFileTmp () {
		if (!$SID = Y('Request.Post.session')) {
			if (!$SID = Y('Helper.list.Session.ID')) {
				return false;
			}
		}

		// ついでに１日以上経過しているテンポラリも削除
		Y('Helper.loadAction', 'ConnectDB', array(
			'query'=>'delete',
			'table'=>'upload_tmp',
			'data'=>array(
				'Session'=>$SID,
				'RegistTime'=>time() - 86400,
			),
			'where'=>'Session=:Session OR RegistTime<:RegistTime'
		));
		return true;
	}

	/**
	 * テンポラリ上のファイルを保存する
	 */
	public function saveFile ($TargetID = null, $FileName = null) {
		Y('Logger.Debug', 'ファイルセーブ開始 TargetID='.$TargetID);
		if (!$SID = Y('Request.Post.session')) {
			if (!$SID = Y('Helper.list.Session.ID')) {
				return false;
			}
		}

		Y('Helper.loadAction', 'ConnectDB', array(
			'query'=>'select',
			'table'=>'upload_tmp',
			'data'=>array(
				'Session'=>$SID,
			),
			'column'=>'ID'
		));

		$FileList = Y('Helper.list.connectDB.com.resultAll');
		foreach ($FileList as $Key=>$File) {
			if (is_array($FileName) && isset ($FileName[$Key])) {
				$NewName = $FileName[$Key];
			} elseif ($FileName) {
				if ($Key == 0) {
					$NewName = $FileName;
				} else {
					break;
				}
			} else {
				$NewName = null;
			}
			$File['TargetID'] = $TargetID;
			Y('Helper.list.ConnectDB', array(
				'query'=>'select',
				'table'=>'upload_tmp',
				'data'=>$File,
				'limit'=>1
			));
			if (!$Data = Y('Helper.list.connectDB.com.result')) break;
			unset ($Data['ID']);
			if ($TargetID) $Data['TargetID'] = $TargetID;

			if ($NewName) $File['Name'] = $NewName;
			$Dupl = array(
				'Target'=>$Data['Target'],
				'Field'=>$Data['Field'],
				'Name'=>$Data['Name'],
			);
			if ($TargetID) $Dupl['TargetID'] = $TargetID;

			// ファイル重複チェック
			Y('Helper.list.ConnectDB', array(
				'query'=>'select',
				'table'=>'upload_table',
				'data'=>$Dupl,
				'column'=>'ID',
				'limit'=>1
			));
			if ($Dupl = Y('Helper.list.connectDB.com.result')){
				//上書き時はキャッシュを削除する。
				$this->deleteCache($Data['Target'], $Data['Field'], $TargetID);
				Y('Helper.list.ConnectDB', array(
					'query'=>'update',
					'table'=>'upload_table',
					'data'=>$Data,
					'where'=>'ID='.(int)$Dupl['ID'],
				));
			} else {
				Y('Helper.list.ConnectDB', array(
					'query'=>'insert',
					'table'=>'upload_table',
					'data'=>$Data,
				));
			}
		}
		// テンポラリを削除
		$this->deleteFileTmp();
		return true;
	}

	/**
	 * アップロード済みのファイルを削除する
	 */
	public function deleteFile ($Field, $Target, $TargetID, $FileName) {
		//Targetその他の指定はセキュリティ上、アドレスのみですべてのファイルを削除できないようにするため。
		if (!$SID = Y('Request.Post.session')) {
			if (!$SID = Y('Helper.list.Session.ID')) {
				return false;
			}
		}

		$Data = array (
			'Session'=>$SID,
			'Target'=>$Target,
		);

		if ($FileName != '*') {
			$Data['Name'] = $FileName;

		}
		if ($Field != '*') {
			$Data['Field'] = $Field;

		}
		if ($TargetID != '*') {
			$Data['TargetID'] = $TargetID;
		}

		// とりあえずキャッシュを削除する
		// 限定しづらいのでTargetのみ指定
		$this->deleteCache($Data['Target']);
		
		// テンポラリ上の同名ファイルを削除する
		Y('Helper.loadAction', 'ConnectDB', array(
			'query'=>'delete',
			'table'=>'upload_tmp',
			'data'=>$Data,
		));

		// 本テーブル上の同名ファイルを削除する
		Y('Helper.loadAction', 'ConnectDB', array(
			'query'=>'delete',
			'table'=>'upload_table',
			'data'=>$Data,
		));
		
		return true;
	}

	/**
	 * キャッシュを削除する
	 */
	private function deleteCache ($Target, $Field = null, $TargetID = null) {
		$Dir = DIR_BASE_HTML.'/vars/cache/upload/'.$Target.'/';

		if ($Field) $Dir .= $Field. '/';
		if ($TargetID) $Dir .= $TargetID. '/';

		if (is_dir ($Dir)) {
			Y('Module.load', 'vars');
			Y('Module.list.Vars.removeDirectory', $Dir);
		}
	}


	/**
	 * admin処理
	 */
	public function callAdmin () {
		$Method = 'callAdmin_'.Y('Request.Global.Act');
		if (method_exists($this, $Method)) {
			$this->$Method ();
		}
	}
	
	public function callAdmin_List () {
		Y('Document.Load', 'upload:upload/admin/list', Y('Document.toObject$'));
	}

}
?>